/*
  LICENSE
  -------
Copyright 2005 Nullsoft, Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, 
are permitted provided that the following conditions are met:

  * Redistributions of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer. 

  * Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution. 

  * Neither the name of Nullsoft nor the names of its contributors may be used to 
    endorse or promote products derived from this software without specific prior written permission. 
 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/
#include <stdio.h>
#include "yuv.h"
#include "support.h"

bool CopyRGBSurfaceToYUVSurfaceMMX(
        LPDDSURFACEDESC2 pddsd1,
        LPDDSURFACEDESC2 pddsd2,
		fourcc_enum eOverlayFormat,
		bool bPentium3,
		bool bDisplayWarnings)
{
	char buf[256];

    if (pddsd1->dwWidth != pddsd2->dwWidth) 
	{
		sprintf(buf, "%s%s", "ERROR in CopyRGBSurfaceToYUVSurfaceMMX: ", "surface widths differ!");
		if (bDisplayWarnings) MessageBox(NULL, buf, "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
        return false;
	}
    if (pddsd1->dwHeight != pddsd2->dwHeight) 
	{
		sprintf(buf, "%s%s", "ERROR in CopyRGBSurfaceToYUVSurfaceMMX: ", "surface heights differ!");
		if (bDisplayWarnings) MessageBox(NULL, buf, "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
        return false;
	}
    if (pddsd1->ddpfPixelFormat.dwRGBBitCount != 16 && pddsd1->ddpfPixelFormat.dwRGBBitCount != 32) 
	{
		sprintf(buf, "%s%s", "ERROR in CopyRGBSurfaceToYUVSurfaceMMX: ", "source surface must be 16 or 32 bits!");
		if (bDisplayWarnings) MessageBox(NULL, buf, "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
        return false;
	}
    if (pddsd2->ddpfPixelFormat.dwRGBBitCount != 16) 
	{
		sprintf(buf, "%s%s", "ERROR in CopyRGBSurfaceToYUVSurfaceMMX: ", "destination surface must be 16 bits!");
		if (bDisplayWarnings) MessageBox(NULL, buf, "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
        return false;
	}
    
	bool b16BitSrc = (pddsd1->ddpfPixelFormat.dwRGBBitCount == 16);
	//bool b16Bit555 = (pddsd1->ddpfPixelFormat.dwGBitMask == 0x03E0);		// 5-6-5: green mask is 0x07E0
	signed __int64 green_bits_plus_5  =  5;
	signed __int64 green_bits_plus_2  =  2;
	signed __int64 green_bits_minus_3 = -3;

	DWORD  w = pddsd1->dwWidth;
    DWORD  h = pddsd1->dwHeight;
    LONG   pitch1 = pddsd1->lPitch;
    LONG   pitch2 = pddsd2->lPitch;
    unsigned __int32 *pPixels1 = (unsigned __int32 *)pddsd1->lpSurface;
    unsigned __int32 *pPixels2 = (unsigned __int32 *)pddsd2->lpSurface;
	signed __int16 _c_m_1[4];
	signed __int16 _c_m_2[4];
	signed __int16 _c_m_3[4];
	signed __int16 _c_m_4[4];
	int loops_per_scanline				= (b16BitSrc) ? w/4 : w/2;
	int bytes_per_scanline_src			= (b16BitSrc) ? w*2 : w*4;
	int bytes_per_scanline_dest			= (w*2);		// YUV-style ~ 16 bpp
	int extra_bytes_per_scanline_src	= pitch1 - bytes_per_scanline_src; 
	int extra_bytes_per_scanline_dest	= pitch2 - bytes_per_scanline_dest;

	unsigned __int16 r_mask4[4];
	unsigned __int16 g_mask4[4];
	unsigned __int16 b_mask4[4];

    if (eOverlayFormat == UYVY) // U Y V Y
    {
		// swap 0<->1, and 2<->3
		_c_m_1[1] = 77/2;  _c_m_1[0] = -38/2;   _c_m_1[3] = 77/2;  _c_m_1[2] = 110/2;
		_c_m_2[1] = 150/2; _c_m_2[0] = -74/2;   _c_m_2[3] = 150/2; _c_m_2[2] = -92/2;
		_c_m_3[1] = 29/2;  _c_m_3[0] = 112/2;   _c_m_3[3] = 29/2;  _c_m_3[2] = -18/2;
		_c_m_4[1] = 0;     _c_m_4[0] = 32768/2; _c_m_4[3] = 0;     _c_m_4[2] = 32768/2;
    }
    else    // Y U Y 2
    {
		// (laptop)
		_c_m_1[0] = 77/2;  _c_m_1[1] = -38/2;   _c_m_1[2] = 77/2;  _c_m_1[3] = 110/2;
		_c_m_2[0] = 150/2; _c_m_2[1] = -74/2;   _c_m_2[2] = 150/2; _c_m_2[3] = -92/2;
		_c_m_3[0] = 29/2;  _c_m_3[1] = 112/2;   _c_m_3[2] = 29/2;  _c_m_3[3] = -18/2;
		_c_m_4[0] = 0;     _c_m_4[1] = 32768/2; _c_m_4[2] = 0;     _c_m_4[3] = 32768/2;
    }

	if (b16BitSrc)
	{
		for (int i=0; i<4; i++)
		{
			r_mask4[i] = (unsigned __int16)(pddsd1->ddpfPixelFormat.dwRBitMask);
			g_mask4[i] = (unsigned __int16)(pddsd1->ddpfPixelFormat.dwGBitMask);
			b_mask4[i] = (unsigned __int16)(pddsd1->ddpfPixelFormat.dwBBitMask);
		}		

		for (i=0; i<32; i++)
			if (pddsd1->ddpfPixelFormat.dwGBitMask & (1 << i)) 
			{
				green_bits_plus_5++;
				green_bits_plus_2++;
				green_bits_minus_3++;
			}
	}

	/*
    if (b16BitSrc && bPentium3) 
	//for (int rep=0; rep<16; rep++)
	__asm
	{
		mov   edx, h

		mov   esi, pPixels1
		mov   edi, pPixels2
		sub   edi, 8			// pre-subtract
		//movq  mm4, _c_m_4
		//movq  mm5, _c_m_1
		//movq  mm6, _c_m_2
		//movq  mm7, _c_m_3
		pxor  mm7, mm7

		ALIGN 8
		yuvscanlineloop16p3:

			mov   ecx, loops_per_scanline

				movq mm3, qword ptr [esi]
				movq mm1, mm3
				movq mm2, mm3
				pand mm3, r_mask4;	// rrrrr000 00000000 rrrrr000 00000000 -------- -------- -------- --------
				pand mm1, g_mask4;	// 00000ggg ggg00000 00000ggg ggg00000 -------- -------- -------- --------
				pand mm2, b_mask4;	// 00000000 000bbbbb 00000000 000bbbbb -------- -------- -------- --------

				//pslld mm1, 5;		// gggggg00 00000000 gggggg00 00000000 -------- -------- -------- --------
				//pslld mm2, green_bits_plus_5;
				psrld mm3, green_bits_plus_2
				psrld mm1, green_bits_minus_3
				pslld mm2, 3

			ALIGN 8
			yuvloop16p3:

				// read in 2 pixels (into mm0, mm1, and mm2 - 3 copies)

                // quick reference:
                // punpcklbw mm7, mm7     // abcdefgh -> eeffgghh
                // punpcklbw mm7, mm0     // abcdefgh -> 0e0f0g0h (if mm0 is zero)
                // punpckhbw mm7, mm7     // abcdefgh -> aabbccdd
                // punpckhbw mm7, mm0     // abcdefgh -> 0a0b0c0d (if mm0 is zero)
				// packuswb mm7, mm7      // 0a0b0g0r -> abgrabgr
				
				//psllq mm0, 0;		// rrrrr000 00000000 rrrrr000 00000000 -------- -------- -------- --------
									// bbbbb000 00000000 bbbbb000 00000000 -------- -------- -------- --------
					//movq  mm0, mm3    

					// mm3: 00 r1 00 r2 00 r3 00 r4
					// mm1: 00 g1 00 g2 00 g3 00 g4
					// mm2: 00 b1 00 b2 00 b3 00 b4

				pshufw mm4, mm3, 0x50	// mm4 <- 00 r3 00 r3 00 r4 00 r4
add  edi, 8
				pshufw mm0, mm3, 0xFA	// mm0 <- 00 r1 00 r1 00 r2 00 r2
				 pmullw mm4, _c_m_1
add  esi, 8
				pshufw mm5, mm1, 0x50	// mm5 <- 00 g3 00 g3 00 g4 00 g4 
				 pmullw mm0, _c_m_1
				  paddsw mm4, _c_m_4
				pshufw mm1, mm1, 0xFA	// mm1 <- 00 g1 00 g1 00 g2 00 g2
				 pmullw mm5, _c_m_2
				  paddsw mm0, _c_m_4
				pshufw mm6, mm2, 0x50	// mm6 <- 00 b3 00 b3 00 b4 00 b4 
				 pmullw mm1, _c_m_2
				  paddsw mm4, mm5
				pshufw mm2, mm2, 0xFA	// mm2 <- 00 b1 00 b1 00 b2 00 b2
				 pmullw mm6, _c_m_3
				  paddsw mm0, mm1
					 movq mm3, qword ptr [esi]	// start loading pixels for next loop
				 pmullw mm2, _c_m_3
				  paddsw mm4, mm6
					 movq mm1, mm3
				  paddsw mm0, mm2
				   psrlw    mm4, 7
					 pand mm1, g_mask4;	// 00000ggg ggg00000 00000ggg ggg00000 -------- -------- -------- --------
				   packuswb mm4, mm4    // bytes: abgrabgr
					 movq mm2, mm3
				   psrlw    mm0, 7
				    psrlq mm4, 32		// COMBINE RESULTS
					 pand mm3, r_mask4;	// rrrrr000 00000000 rrrrr000 00000000 -------- -------- -------- --------
				   packuswb mm0, mm0    // bytes: abgrabgr
					 pand mm2, b_mask4;	// 00000000 000bbbbb 00000000 000bbbbb -------- -------- -------- --------
				    
					 psrld mm3, green_bits_plus_2
				    psllq mm0, 32
					 psrld mm1, green_bits_minus_3
				    por   mm0, mm4
					 pslld mm2, 3
				    movq  dword ptr[edi], mm0

				// step 2: multiply to get to this state:
				//	mm0: r1*c1r1  r1*c1r2  r2*c2r1  r2*c2r2
				//	mm1: g1*c1g1  g1*c1g2  g2*c2g1  g2*c2g2
				//	mm2: b1*c1b1  b1*c1b2  b2*c2b1  b2*c2b2
				//  mm4: 0        32k      0        32k
				
				// step 3: add to get to this state:
				//	mm0: d1*256   d2*256   d3*256   d4*256
							
				dec ecx
				jnz yuvloop16p3

			// scanline complete
			add esi, extra_bytes_per_scanline_src
			add edi, extra_bytes_per_scanline_dest

			dec edx
			jnz yuvscanlineloop16p3

		emms
	}
    else if (b16BitSrc && !bPentium3) */
	if (b16BitSrc)
	__asm
	{
		mov   edx, h

		mov   esi, pPixels1
		mov   edi, pPixels2
		sub   edi, 8			// pre-subtract
		//movq  mm4, _c_m_4
		//movq  mm5, _c_m_1
		//movq  mm6, _c_m_2
		//movq  mm7, _c_m_3
		pxor  mm7, mm7

		ALIGN 8
		yuvscanlineloop16:

			mov   ecx, loops_per_scanline

				movq mm3, qword ptr [esi]
				movq mm1, mm3
				movq mm2, mm3
				pand mm3, r_mask4;	// rrrrr000 00000000 rrrrr000 00000000 -------- -------- -------- --------
				pand mm1, g_mask4;	// 00000ggg ggg00000 00000ggg ggg00000 -------- -------- -------- --------
				pand mm2, b_mask4;	// 00000000 000bbbbb 00000000 000bbbbb -------- -------- -------- --------

				//pslld mm1, 5;		// gggggg00 00000000 gggggg00 00000000 -------- -------- -------- --------
				//pslld mm2, green_bits_plus_5;
				psrld mm3, green_bits_plus_2
				psrld mm1, green_bits_minus_3
				pslld mm2, 3

			ALIGN 8
			yuvloop16:

				// read in 2 pixels (into mm0, mm1, and mm2 - 3 copies)

                // quick reference:
                // punpcklbw mm7, mm7     // abcdefgh -> eeffgghh
                // punpcklbw mm7, mm0     // abcdefgh -> 0e0f0g0h (if mm0 is zero)
                // punpckhbw mm7, mm7     // abcdefgh -> aabbccdd
                // punpckhbw mm7, mm0     // abcdefgh -> 0a0b0c0d (if mm0 is zero)
				// packuswb mm7, mm7      // 0a0b0g0r -> abgrabgr
				
				//psllq mm0, 0;		// rrrrr000 00000000 rrrrr000 00000000 -------- -------- -------- --------
									// bbbbb000 00000000 bbbbb000 00000000 -------- -------- -------- --------
					movq  mm0, mm3    

					// mm0: 00 r1 00 r2 00 r3 00 r4
					// mm1: 00 g1 00 g2 00 g3 00 g4
					// mm2: 00 b1 00 b2 00 b3 00 b4
					packuswb  mm0, mm0
					packuswb  mm1, mm1
					packuswb  mm2, mm2
					// mm0: r1 r2 r3 r4 r1 r2 r3 r4
					// mm1: g1 g2 g3 g4 g1 g2 g3 g4
					// mm2: b1 b2 b3 b4 b1 b2 b3 b4
					punpckhbw mm0, mm0
					punpckhbw mm1, mm1
									movq  mm4, mm0	// -- -- -- -- r3 r3 r4 r4 
					punpckhbw mm2, mm2
					// mm0: r1 r1 r2 r2 r3 r3 r4 r4
					// mm1: g1 g1 g2 g2 g3 g3 g4 g4
					// mm2: b1 b1 b2 b2 b3 b3 b4 b4
									movq  mm5, mm1	// -- -- -- -- g3 g3 g4 g4 

				// WORK ON MM0-2	WORK ON MM4-6
				// -------------    -------------
				punpckhbw mm0, mm7	// 00 r1 00 r1 00 r2 00 r2
									movq  mm6, mm2	// -- -- -- -- b3 b3 b4 b4 
				punpckhbw mm1, mm7	// 00 g1 00 g1 00 g2 00 g2
									punpcklbw mm4, mm7	// 00 r3 00 r3 00 r4 00 r4
				punpckhbw mm2, mm7	// 00 b1 00 b1 00 b2 00 b2

				// step 2: multiply to get to this state:
				//	mm0: r1*c1r1  r1*c1r2  r2*c2r1  r2*c2r2
				//	mm1: g1*c1g1  g1*c1g2  g2*c2g1  g2*c2g2
				//	mm2: b1*c1b1  b1*c1b2  b2*c2b1  b2*c2b2
				//  mm4: 0        32k      0        32k
				pmullw mm0, _c_m_1
									punpcklbw mm5, mm7	// 00 g3 00 g3 00 g4 00 g4
				 add  edi, 8
				pmullw mm1, _c_m_2
									punpcklbw mm6, mm7	// 00 b3 00 b3 00 b4 00 b4
				 add  esi, 8
				pmullw mm2, _c_m_3
									pmullw mm4, _c_m_1
				
				// step 3: add to get to this state:
				//	mm0: d1*256   d2*256   d3*256   d4*256
				paddsw mm0, _c_m_4
									pmullw mm5, _c_m_2
				paddsw mm0, mm1
									pmullw mm6, _c_m_3
				paddsw mm0, mm2
				psrlw    mm0, 7
									paddsw mm4, _c_m_4
				packuswb mm0, mm0    // bytes: abgrabgr
				//movd     dword ptr [edi], mm0  // store 
									paddsw mm4, mm5
													movq mm2, qword ptr [esi]
													movq mm1, mm2
									paddsw mm4, mm6
													movq mm3, mm2
													pand mm3, r_mask4;	// rrrrr000 00000000 rrrrr000 00000000 -------- -------- -------- --------

									psrlw    mm4, 7
													pand mm1, g_mask4;	// 00000ggg ggg00000 00000ggg ggg00000 -------- -------- -------- --------
									packuswb mm4, mm4    // bytes: abgrabgr
									//movd     dword ptr [edi+4], mm4  // store 
													pand mm2, b_mask4;	// 00000000 000bbbbb 00000000 000bbbbb -------- -------- -------- --------

							// COMBINE RESULTS									
							psllq mm0, 32
													psrld mm3, green_bits_plus_2
													////pslld mm1, 5;		// gggggg00 00000000 gggggg00 00000000 -------- -------- -------- --------
													////pslld mm2, green_bits_plus_5;
													//psrld mm3, green_bits_plus_2
													//psrld mm1, green_bits_minus_3
													//pslld mm2, 3
							psrlq mm4, 32
													psrld mm1, green_bits_minus_3
							por   mm0, mm4
													pslld mm2, 3
							movq  dword ptr[edi], mm0
							
				dec ecx
				jnz yuvloop16

			// scanline complete
			add esi, extra_bytes_per_scanline_src
			add edi, extra_bytes_per_scanline_dest

			dec edx
			jnz yuvscanlineloop16

		emms
	}
	else __asm	// 32-bpp source surface
	{
		mov   edx, h

		mov   esi, pPixels1
		mov   edi, pPixels2
		sub   edi, 4			// pre-subtract
		movq  mm4, _c_m_4
		movq  mm5, _c_m_1
		movq  mm6, _c_m_2
		movq  mm7, _c_m_3

		ALIGN 8
		yuvscanlineloop32:

			mov   ecx, loops_per_scanline

			ALIGN 8
			yuvloop32:

                /*
                // prefetch
			    test     ecx, 0x000001ff
			    jnz      PROCESS_PIXEL_MMX32           // every 256th pixel do some prefetches

						 mov      ebx, 2*256                 // need to prefetch 256*6 bytes
                        ALIGN 8
					    LOAD_ESI_ARRAY_MMX32:
						 mov      eax, [ebx+esi]
						 mov      eax, [ebx+esi+32]
						 sub      ebx, 64
						 jnz      LOAD_ESI_ARRAY_MMX32

                ALIGN 8
				PROCESS_PIXEL_MMX32:
                */

				// read in 2 pixels
				movq mm0, qword ptr [esi]	// -- b1 g1 r1 -- b2 g2 r2
				movq mm1, qword ptr [esi]	// -- b1 g1 r1 -- b2 g2 r2
				movq mm2, qword ptr [esi]	// -- b1 g1 r1 -- b2 g2 r2

                // quick reference:
                // punpcklbw mm7, mm7     // abcdefgh -> eeffgghh
                // punpcklbw mm7, mm0     // abcdefgh -> 0e0f0g0h (if mm0 is zero)
				// packuswb mm7, mm7      // 0a0b0g0r -> abgrabgr ?

				// step 1: get to this state:
				//	mm0: r1   r1   r2   r2
				//	mm1: g1   g1   g2   g2
				//	mm2: b1   b1   b2   b2
				//  mm3: junk
				//  mm4: 0    32k  0    32k
				//  mm5: c1r1 c1r2 c2r1 c2r2
				//  mm6: c1g1 c1g2 c2g1 c2g2
				//  mm7: c1b1 c1b2 c2b1 c2b2

				// NOTE: the shifts of 8, 16, and 24 below are
				//       correct (vs. 0-8-16) but might be in
				//       backwards order!

				pslld mm0, 8
				pslld mm1, 16
				pslld mm2, 24
				psrld mm0, 24    // 00 00 00 r1 00 00 00 r2
				psrld mm1, 24    // 00 00 00 g1 00 00 00 g2
				psrld mm2, 24    // 00 00 00 b1 00 00 00 b2
				
                movq  mm3, mm0
				pslld mm0, 16
				por   mm0, mm3   // 00 r1 00 r1 00 r2 00 r2

				movq  mm3, mm1
				pslld mm1, 16
				por   mm1, mm3   // 00 g1 00 g1 00 g2 00 g2

				movq  mm3, mm2
				pslld mm2, 16
				por   mm2, mm3   // 00 b1 00 b1 00 b2 00 b2

				// step 2: multiply to get to this state:
				//	mm0: r1*c1r1  r1*c1r2  r2*c2r1  r2*c2r2
				//	mm1: g1*c1g1  g1*c1g2  g2*c2g1  g2*c2g2
				//	mm2: b1*c1b1  b1*c1b2  b2*c2b1  b2*c2b2
				//  mm4: 0    32k  0    32k
				pmullw mm0, mm5
				 add  edi, 4
				pmullw mm1, mm6
				 add  esi, 8
				pmullw mm2, mm7
				
				// step 3: add to get to this state:
				//	mm0: d1*256   d2*256   d3*256   d4*256
				paddsw mm0, mm4
				paddsw mm0, mm1
				paddsw mm0, mm2

				psrlw    mm0, 7
				packuswb mm0, mm0    // bytes: abgrabgr
				movd     dword ptr [edi], mm0  // store 
				
				loop yuvloop32

			// scanline complete
			add esi, extra_bytes_per_scanline_src
			add edi, extra_bytes_per_scanline_dest

			dec edx
			jnz yuvscanlineloop32

		emms
	}

	/*
	// THE ORIGINAL, MILDLY-INTERLEAVED CODE:
	// THE ORIGINAL, MILDLY-INTERLEAVED CODE:
	// THE ORIGINAL, MILDLY-INTERLEAVED CODE:
    if (b16BitSrc) __asm
	{
		mov   edx, h

		mov   esi, pPixels1
		mov   edi, pPixels2
		sub   edi, 8			// pre-subtract
		//movq  mm4, _c_m_4
		//movq  mm5, _c_m_1
		//movq  mm6, _c_m_2
		//movq  mm7, _c_m_3
		pxor  mm7, mm7

		ALIGN 8
		yuvscanlineloop16:

			mov   ecx, loops_per_scanline

			ALIGN 8
			yuvloop16:

				// read in 2 pixels (into mm0, mm1, and mm2 - 3 copies)
				movq mm0, qword ptr [esi]
				movq mm1, qword ptr [esi]
				movq mm2, qword ptr [esi]

				// step 1: get to this state:
				//	mm0: r1   r1   r2   r2
				//	mm1: g1   g1   g2   g2
				//	mm2: b1   b1   b2   b2

                // quick reference:
                // punpcklbw mm7, mm7     // abcdefgh -> eeffgghh
                // punpcklbw mm7, mm0     // abcdefgh -> 0e0f0g0h (if mm0 is zero)
				// packuswb mm7, mm7      // 0a0b0g0r -> abgrabgr ?
				
				pand mm0, r_mask4;	// rrrrr000 00000000 rrrrr000 00000000 -------- -------- -------- --------
				pand mm1, g_mask4;	// 00000ggg ggg00000 00000ggg ggg00000 -------- -------- -------- --------
				pand mm2, b_mask4;	// 00000000 000bbbbb 00000000 000bbbbb -------- -------- -------- --------

				//psllq mm0, 0;		// rrrrr000 00000000 rrrrr000 00000000 -------- -------- -------- --------
				pslld mm1, 5;		// gggggg00 00000000 gggggg00 00000000 -------- -------- -------- --------
				pslld mm2, green_bits_plus_5;
									// bbbbb000 00000000 bbbbb000 00000000 -------- -------- -------- --------
                
					movq  mm4, mm0
					movq  mm5, mm1
					movq  mm6, mm2
					psrld mm0, 8
					psrld mm1, 8
					psrld mm2, 8
					por   mm0, mm4  // r1 r1 r2 r2 -- -- -- --
					por   mm1, mm5  // g1 g1 g2 g2 -- -- -- --
					por   mm2, mm6  // b1 b1 b2 b2 -- -- -- --
									movq  mm4, mm0	// -- -- -- -- r3 r3 r4 r4 
									movq  mm5, mm1	// -- -- -- -- g3 g3 g4 g4 
									movq  mm6, mm2	// -- -- -- -- b3 b3 b4 b4 

				// WORK ON MM0-2	WORK ON MM4-6
				// -------------    -------------
				punpckhbw mm0, mm7	// 00 r1 00 r1 00 r2 00 r2
									psllq mm4, 32		// r3 r3 r4 r4 -- -- -- --
				punpckhbw mm1, mm7	// 00 g1 00 g1 00 g2 00 g2
									psllq mm5, 32		// g3 g3 g4 g4 -- -- -- --
				punpckhbw mm2, mm7	// 00 b1 00 b1 00 b2 00 b2
									psllq mm6, 32		// b3 b3 b4 b4 -- -- -- --

				// step 2: multiply to get to this state:
				//	mm0: r1*c1r1  r1*c1r2  r2*c2r1  r2*c2r2
				//	mm1: g1*c1g1  g1*c1g2  g2*c2g1  g2*c2g2
				//	mm2: b1*c1b1  b1*c1b2  b2*c2b1  b2*c2b2
				//  mm4: 0        32k      0        32k
				pmullw mm0, _c_m_1
									punpckhbw mm4, mm7	// 00 r3 00 r3 00 r4 00 r4
				 add  edi, 8
				pmullw mm1, _c_m_2
									punpckhbw mm5, mm7	// 00 g3 00 g3 00 g4 00 g4
				 add  esi, 8
				pmullw mm2, _c_m_3
									punpckhbw mm6, mm7	// 00 b3 00 b3 00 b4 00 b4
				
				// step 3: add to get to this state:
				//	mm0: d1*256   d2*256   d3*256   d4*256
				paddsw mm0, _c_m_4
									pmullw mm4, _c_m_1
				paddsw mm0, mm1
									pmullw mm5, _c_m_2
				paddsw mm0, mm2
									pmullw mm6, _c_m_3

				psrlw    mm0, 7
									paddsw mm4, _c_m_4
				packuswb mm0, mm0    // bytes: abgrabgr
									paddsw mm4, mm5
				//movd     dword ptr [edi], mm0  // store 

									paddsw mm4, mm6

									psrlw    mm4, 7
									packuswb mm4, mm4    // bytes: abgrabgr
									//movd     dword ptr [edi+4], mm4  // store 

							// COMBINE RESULTS									
							psllq mm0, 32
							psrlq mm4, 32
							por   mm0, mm4
							movq  dword ptr[edi], mm0
				
				dec ecx
				jnz yuvloop16

			// scanline complete
			add esi, extra_bytes_per_scanline_src
			add edi, extra_bytes_per_scanline_dest

			dec edx
			jnz yuvscanlineloop16

		emms
	}
	*/
	/*
	THE ORIGINAL NON-INTERLEAVED, NON-OPTIMIZED, 16-BIT ROUTINE.
	NOTE: requires loops_per_scanline to be w/2

    if (b16BitSrc) __asm
	{
		mov   edx, h

		mov   esi, pPixels1
		mov   edi, pPixels2
		//sub   edi, 8			// pre-subtract
		//movq  mm4, _c_m_4
		//movq  mm5, _c_m_1
		//movq  mm6, _c_m_2
		//movq  mm7, _c_m_3
		pxor  mm7, mm7

		ALIGN 8
		yuvscanlineloop16:

			mov   ecx, loops_per_scanline

			ALIGN 8
			yuvloop16:

				// read in 2 pixels (into mm0, mm1, and mm2 - 3 copies)
				movq mm0, qword ptr [esi]
				movq mm1, qword ptr [esi]
				movq mm2, qword ptr [esi]

				// step 1: get to this state:
				//	mm0: r1   r1   r2   r2
				//	mm1: g1   g1   g2   g2
				//	mm2: b1   b1   b2   b2

                // quick reference:
                // punpcklbw mm7, mm7     // abcdefgh -> eeffgghh
                // punpcklbw mm7, mm0     // abcdefgh -> 0e0f0g0h (if mm0 is zero)
				// packuswb mm7, mm7      // 0a0b0g0r -> abgrabgr ?
				
				pand mm0, r_mask4;	// rrrrr000 00000000 rrrrr000 00000000 -------- -------- -------- --------
				pand mm1, g_mask4;	// 00000ggg ggg00000 00000ggg ggg00000 -------- -------- -------- --------
				pand mm2, b_mask4;	// 00000000 000bbbbb 00000000 000bbbbb -------- -------- -------- --------

					//psllq mm0, 0;		// rrrrr000 00000000 rrrrr000 00000000 -------- -------- -------- --------
					pslld mm1, 5;		// gggggg00 00000000 gggggg00 00000000 -------- -------- -------- --------
					pslld mm2, green_bits_plus_5;// 00000000 bbbbb000 00000000 -------- -------- -------- --------
					movq  mm4, mm0
					movq  mm5, mm1
					movq  mm6, mm2
					psrld mm0, 8
					psrld mm1, 8
					psrld mm2, 8
					por   mm0, mm4  // r1 r1 r2 r2 -- -- -- --
					por   mm1, mm5  // g1 g1 g2 g2 -- -- -- --
					por   mm2, mm6  // b1 b1 b2 b2 -- -- -- --
					punpckhbw mm0, mm7	// 00 r1 00 r1 00 r2 00 r2
					punpckhbw mm1, mm7	// 00 g1 00 g1 00 g2 00 g2
					punpckhbw mm2, mm7	// 00 b1 00 b1 00 b2 00 b2

					// ALTERNATE:
					//psrlq mm0, 32+8
					//psrlq mm1, 32+3
					//psllq mm2,green_bits_plus_29
					//punpcklbw mm0, mm7
					//punpcklbw mm1, mm7
					//punpcklbw mm2, mm7
					//packuswb mm0, mm0	
					//packuswb mm1, mm1
					//packuswb mm2, mm2


				// step 2: multiply to get to this state:
				//	mm0: r1*c1r1  r1*c1r2  r2*c2r1  r2*c2r2
				//	mm1: g1*c1g1  g1*c1g2  g2*c2g1  g2*c2g2
				//	mm2: b1*c1b1  b1*c1b2  b2*c2b1  b2*c2b2
				//  mm4: 0        32k      0        32k
				pmullw mm0, _c_m_1
				 add  edi, 4
				pmullw mm1, _c_m_2
				 add  esi, 4
				pmullw mm2, _c_m_3
				
				// step 3: add to get to this state:
				//	mm0: d1*256   d2*256   d3*256   d4*256
				paddsw mm0, _c_m_4
				paddsw mm0, mm1
				paddsw mm0, mm2

				psrlw    mm0, 7
				packuswb mm0, mm0    // bytes: abgrabgr
				movd     dword ptr [edi], mm0  // store 
				
				dec ecx
				jnz yuvloop16

			// scanline complete
			add esi, extra_bytes_per_scanline_src
			add edi, extra_bytes_per_scanline_dest

			dec edx
			jnz yuvscanlineloop16

		emms
	}
	*/
	
    return true;
}

/*
oops - only works w/YUV overlays!!!

bool CopyRGBSurfaceToYUVSurfaceMMXAndSaturate(
        LPDDSURFACEDESC pddsd1,
        LPDDSURFACEDESC pddsd2,
		fourcc_enum eOverlayFormat,
		int saturation)
{
	// saturation: 0 = none, 255 = full
	if (saturation==0)
		return CopyRGBSurfaceToYUVSurfaceMMX(pddsd1, pddsd2, eOverlayFormat);
    
	if (pddsd1->dwWidth != pddsd2->dwWidth) 
        return false;
    if (pddsd1->dwHeight != pddsd2->dwHeight) 
        return false;
    
    DWORD  w = pddsd1->dwWidth;
    DWORD  h = pddsd1->dwHeight;
    LONG   pitch1 = pddsd1->lPitch;
    LONG   pitch2 = pddsd2->lPitch;
    unsigned __int32 *pPixels1 = (unsigned __int32 *)pddsd1->lpSurface;
    unsigned __int32 *pPixels2 = (unsigned __int32 *)pddsd2->lpSurface;
	signed __int16 _c_m_1[4];
	signed __int16 _c_m_2[4];
	signed __int16 _c_m_3[4];
	signed __int16 _c_m_4[4];
	int loops_per_scanline = w/2;
	int extra_bytes_per_scanline_src  = pitch1 - w*4;
	int extra_bytes_per_scanline_dest = pitch2 - w*2;
	unsigned __int16 m_sat[4] = { saturation, saturation, saturation, saturation };

    if (eOverlayFormat == UYVY) // U Y V Y
    {
		// swap 0<->1, and 2<->3
		_c_m_1[1] = 77/2;  _c_m_1[0] = -38/2;   _c_m_1[3] = 77/2;  _c_m_1[2] = 110/2;
		_c_m_2[1] = 150/2; _c_m_2[0] = -74/2;   _c_m_2[3] = 150/2; _c_m_2[2] = -92/2;
		_c_m_3[1] = 29/2;  _c_m_3[0] = 112/2;   _c_m_3[3] = 29/2;  _c_m_3[2] = -18/2;
		_c_m_4[1] = 0;     _c_m_4[0] = 32768/2; _c_m_4[3] = 0;     _c_m_4[2] = 32768/2;
    }
    else    // Y U Y 2
    {
		// (laptop)
		_c_m_1[0] = 77/2;  _c_m_1[1] = -38/2;   _c_m_1[2] = 77/2;  _c_m_1[3] = 110/2;
		_c_m_2[0] = 150/2; _c_m_2[1] = -74/2;   _c_m_2[2] = 150/2; _c_m_2[3] = -92/2;
		_c_m_3[0] = 29/2;  _c_m_3[1] = 112/2;   _c_m_3[2] = 29/2;  _c_m_3[3] = -18/2;
		_c_m_4[0] = 0;     _c_m_4[1] = 32768/2; _c_m_4[2] = 0;     _c_m_4[3] = 32768/2;
    }
    
	__asm
	{
		mov   edx, h

		mov   esi, pPixels1
		mov   edi, pPixels2
		sub   edi, 4			// pre-subtract
		movq  mm4, _c_m_4
		movq  mm5, _c_m_1
		movq  mm6, _c_m_2
		movq  mm7, _c_m_3

		yuvscanlineloop:

			mov   ecx, loops_per_scanline

			yuvloop:

				// read in 2 pixels
				movq mm0, qword ptr [esi]	// -- b1 g1 r1 -- b2 g2 r2
				//movq mm1, qword ptr [esi]	// -- b1 g1 r1 -- b2 g2 r2
				//movq mm2, qword ptr [esi]	// -- b1 g1 r1 -- b2 g2 r2

				movq mm1, qword ptr [esi]
				pmullw mm0, m_sat
				paddusw mm0, mm1
				movq mm1, mm0
				movq mm2, mm0

				//paddusb mm0, mm0
				//paddusb mm1, mm1
				//paddusb mm2, mm2

				// step 1: get to this state:
				//	mm0: r1   r1   r2   r2
				//	mm1: g1   g1   g2   g2
				//	mm2: b1   b1   b2   b2
				//  mm3: junk
				//  mm4: 0    32k  0    32k
				//  mm5: c1r1 c1r2 c2r1 c2r2
				//  mm6: c1g1 c1g2 c2g1 c2g2
				//  mm7: c1b1 c1b2 c2b1 c2b2

				// NOTE: the shifts of 8, 16, and 24 below are
				//       correct (vs. 0-8-16) but might be in
				//       backwards order!
				pslld mm0, 8
				psrld mm0, 24
				movq  mm3, mm0
				pslld mm0, 16
				por   mm0, mm3   // 00 r1 00 r1 00 r2 00 r2

				pslld mm1, 16
				psrld mm1, 24
				 add  edi, 4
				movq  mm3, mm1
				pslld mm1, 16
				por   mm1, mm3   // 00 g1 00 g1 00 g2 00 g2

				pslld mm2, 24
				psrld mm2, 24
				 add  esi, 8
				movq  mm3, mm2
				pslld mm2, 16
				por   mm2, mm3   // 00 b1 00 b1 00 b2 00 b2

				// step 2: multiply to get to this state:
				//	mm0: r1*c1r1  r1*c1r2  r2*c2r1  r2*c2r2
				//	mm1: g1*c1g1  g1*c1g2  g2*c2g1  g2*c2g2
				//	mm2: b1*c1b1  b1*c1b2  b2*c2b1  b2*c2b2
				//  mm4: 0    32k  0    32k
				pmullw mm0, mm5
				pmullw mm1, mm6
				pmullw mm2, mm7
				
				// step 3: add to get to this state:
				//	mm0: d1*256   d2*256   d3*256   d4*256
				paddsw mm0, mm4
				paddsw mm0, mm1
				paddsw mm0, mm2

				psrlw    mm0, 7
				packuswb mm0, mm0    // bytes: abgrabgr
				movd     dword ptr [edi], mm0  // store 
				
				loop yuvloop

			// scanline complete
			add esi, extra_bytes_per_scanline_src
			add edi, extra_bytes_per_scanline_dest

			dec edx
			jnz yuvscanlineloop

		emms
	}
   
    return true;
}
*/


bool CopyRGBSurfaceToYUVSurface(
        LPDDSURFACEDESC2 pddsd1,
        LPDDSURFACEDESC2 pddsd2,
		fourcc_enum eOverlayFormat,
		bool bMMX,
		bool bPentium3,
		bool bDisplayWarnings	)
{
	char buf[256];

	//bMMX = !bMMX;		// <- FOR TESTING

	if (bMMX)
		return CopyRGBSurfaceToYUVSurfaceMMX(pddsd1, pddsd2, eOverlayFormat, bPentium3, bDisplayWarnings);
 
	if (pddsd1->dwWidth != pddsd2->dwWidth) 
	{
		sprintf(buf, "%s%s", "ERROR in CopyRGBSurfaceToYUVSurface: ", "surface widths differ!");
		if (bDisplayWarnings) MessageBox(NULL, buf, "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
        return false;
	}
    if (pddsd1->dwHeight != pddsd2->dwHeight) 
	{
		sprintf(buf, "%s%s", "ERROR in CopyRGBSurfaceToYUVSurface: ", "surface heights differ!");
		if (bDisplayWarnings) MessageBox(NULL, buf, "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
        return false;
	}
    if (pddsd1->ddpfPixelFormat.dwRGBBitCount != 16 && pddsd1->ddpfPixelFormat.dwRGBBitCount != 32) 
	{
		sprintf(buf, "%s%s", "ERROR in CopyRGBSurfaceToYUVSurface: ", "source surface must be 16 or 32 bits!");
		if (bDisplayWarnings) MessageBox(NULL, buf, "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
        return false;
	}
    if (pddsd2->ddpfPixelFormat.dwRGBBitCount != 16) 
	{
		sprintf(buf, "%s%s", "ERROR in CopyRGBSurfaceToYUVSurface: ", "destination surface must be 16 bits!");
		if (bDisplayWarnings) MessageBox(NULL, buf, "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
        return false;
	}

    
    DWORD  w = pddsd1->dwWidth;
    DWORD  h = pddsd1->dwHeight;
	DWORD  bpp1 = pddsd1->ddpfPixelFormat.dwRGBBitCount;
	DWORD  bpp2 = pddsd1->ddpfPixelFormat.dwRGBBitCount;
    LONG   pitch1 = pddsd1->lPitch;
    LONG   pitch2 = pddsd2->lPitch;
    LONG   offset1 = 0;
    LONG   offset2 = 0;
	unsigned __int32 *pPixels1 = (unsigned __int32 *)pddsd1->lpSurface;
	unsigned __int32 *pPixels2 = (unsigned __int32 *)pddsd2->lpSurface;
	unsigned __int32 color1;
    unsigned int    R, G, B, i1, i2, i3, i4;
    BYTE            yuv[4];

    if (eOverlayFormat == UYVY) // U Y V Y
    {
        i1 = 1;
        i2 = 0;
        i3 = 3;
        i4 = 2;
    }
    else    // Y U Y 2
    {
		// (laptop)
        i1 = 0;
        i2 = 1;
        i3 = 2;
        i4 = 3;
    }

	int nShiftBits[3] = { -1,-1,-1 };	// r,g,b
	int nShiftBits2[3] = { -1,-1,-1 };	// r,g,b
	int nBits[3] = { 0, 0, 0 };
	DWORD mask[3] = {	pddsd1->ddpfPixelFormat.dwRBitMask,
						pddsd1->ddpfPixelFormat.dwGBitMask,
						pddsd1->ddpfPixelFormat.dwBBitMask };
	for (int ch=0; ch<3; ch++)
	{
		for (int i=0; i<(int)bpp1; i++)
		{
			if (mask[ch] & (1 << i))
			{
				nBits[ch]++;
				if (nShiftBits[ch] == -1) 
					nShiftBits[ch] = i;
			}
		}
		mask[ch] >>= nShiftBits[ch];
		nShiftBits2[ch] = 8 - nBits[ch];
	}
	
	//						   mask                nShiftBits  nShiftBits2
	// [--RRGGBB]           r: [00FF0000]          16          
	//                      g: [0000FF00]          8           
	//                      b: [000000FF]          0           
	// [-rrrrrgg|gggbbbbb]  r: [01111100|00000000] 10          
	//                      g: [00000011|11100000] 5           
	//                      b: [00000000|00011111] 0           
	// [rrrrrggg|gggbbbbb]  r: [11111000|00000000] 11          
	//                      g: [00000111|11100000] 5           
	//                      b: [00000000|00011111] 0           

    // Go through the image 2 pixels at a time and convert to YUV

	if (bpp1==16)
	{
		if (mask[0]==0x7C00 && mask[1]==0x03E0 && mask[2]==0x001F)
		{
			// 16-bit, 5-5-5
			for (unsigned int y=0; y<h; y++)
			{
				offset1 = y*pitch1/4;
				offset2 = y*pitch2/4;
				for (unsigned int x=0; x<w; x+=2)
				{
					color1 = (pPixels1[offset1] & 0xFFFF);
					B = (color1 & 0x001F) << 3;
					G = (color1 & 0x03E0) >> 2;
					R = (color1 & 0x7C00) >> 8;
					//G = R; B = R;
					//yuv[i1] = (BYTE)(0.299*R + 0.587*G + 0.114*B);
					//yuv[i2] = (BYTE)(128.0 - 0.147*R - 0.289*G + 0.436*B);
					yuv[i1] = (77*R + 150*G + 29*B) >> 8;
					yuv[i2] = (32768 - 38*R - 74*G + 112*B) >> 8;
            
					color1 = ((pPixels1[offset1] >> 16) & 0xFFFF);
					B = (color1 & 0x001F) << 3;
					G = (color1 & 0x03E0) >> 2;
					R = (color1 & 0x7C00) >> 8;
					//G = R; B = R;
					//yuv[i3] = (BYTE)(0.299*R + 0.587*G + 0.114*B);
					//yuv[i4] = (BYTE)(128.0 + 0.36*R - 0.29*G - 0.07*B);
					//yuv[i4] = (BYTE)(128.0 + 0.43*R - 0.36*G - 0.07*B);
					yuv[i3] = (77*R + 150*G + 29*B) >> 8;
					yuv[i4] = (32768 + 110*R - 92*G - 18*B) >> 8;

					offset1++;
					pPixels2[offset2++] = *((unsigned __int32 *)yuv);
				}
			}
		}
		else if (mask[0]==0xF800 && mask[1]==0x07E0 && mask[2]==0x001F)
		{
			// 16-bit, 5-6-5
			for (unsigned int y=0; y<h; y++)
			{
				offset1 = y*pitch1/4;
				offset2 = y*pitch2/4;
				for (unsigned int x=0; x<w; x+=2)
				{
					color1 = (pPixels1[offset1] & 0xFFFF);
					B = (color1 & 0x001F) << 3;
					G = (color1 & 0x07E0) >> 3;
					R = (color1 & 0xF800) >> 8;
					yuv[i1] = (77*R + 150*G + 29*B) >> 8;
					yuv[i2] = (32768 - 38*R - 74*G + 112*B) >> 8;
            
					color1 = ((pPixels1[offset1] >> 16) & 0xFFFF);
					B = (color1 & 0x001F) << 3;
					G = (color1 & 0x07E0) >> 3;
					R = (color1 & 0xF800) >> 8;
					yuv[i3] = (77*R + 150*G + 29*B) >> 8;
					yuv[i4] = (32768 + 110*R - 92*G - 18*B) >> 8;

					offset1++;
					pPixels2[offset2++] = *((unsigned __int32 *)yuv);
				}
			}
		}
		else
		{
			// 16-bit, any generic format
			for (unsigned int y=0; y<h; y++)
			{
				offset1 = y*pitch1/4;
				offset2 = y*pitch2/4;
				for (unsigned int x=0; x<w; x+=2)
				{
					color1 = (pPixels1[offset1] & 0xFFFF);
					B = ((color1 >> nShiftBits[2]) & mask[2]) << nShiftBits2[2];//(color1) & 0xFF;
					G = ((color1 >> nShiftBits[1]) & mask[1]) << nShiftBits2[1];//(color1 >> 8) & 0xFF;
					R = ((color1 >> nShiftBits[0]) & mask[0]) << nShiftBits2[0];//(color1 >> 16) & 0xFF;
					yuv[i1] = (77*R + 150*G + 29*B) >> 8;
					yuv[i2] = (32768 - 38*R - 74*G + 112*B) >> 8;
            
					color1 = ((pPixels1[offset1] >> 16) & 0xFFFF);
					B = ((color1 >> nShiftBits[2]) & mask[2]) << nShiftBits2[2];//(color1) & 0xFF;
					G = ((color1 >> nShiftBits[1]) & mask[1]) << nShiftBits2[1];//(color1 >> 8) & 0xFF;
					R = ((color1 >> nShiftBits[0]) & mask[0]) << nShiftBits2[0];//(color1 >> 16) & 0xFF;
					yuv[i3] = (77*R + 150*G + 29*B) >> 8;
					yuv[i4] = (32768 + 110*R - 92*G - 18*B) >> 8;

					offset1++;
					pPixels2[offset2++] = *((unsigned __int32 *)yuv);
				}
			}
		}
	}
	else if (bpp1==32)
	{
		for (unsigned int y=0; y<h; y++)
		{
			offset1 = y*pitch1/4;
			offset2 = y*pitch2/4;

			for (unsigned int x=0; x<w; x+=2)
			{
				color1 = pPixels1[offset1++];
				B = (color1 & mask[2]) >> nShiftBits[2];//(color1) & 0xFF;
				G = (color1 & mask[1]) >> nShiftBits[1];//(color1 >> 8) & 0xFF;
				R = (color1 & mask[0]) >> nShiftBits[0];//(color1 >> 16) & 0xFF;
				//G = R; B = R;
				//yuv[i1] = (BYTE)(0.299*R + 0.587*G + 0.114*B);
				//yuv[i2] = (BYTE)(128.0 - 0.147*R - 0.289*G + 0.436*B);
				yuv[i1] = (77*R + 150*G + 29*B) >> 8;
				yuv[i2] = (32768 - 38*R - 74*G + 112*B) >> 8;
        
				color1 = pPixels1[offset1++];
				B = (color1 & mask[2]) >> nShiftBits[2];//(color1) & 0xFF;
				G = (color1 & mask[1]) >> nShiftBits[1];//(color1 >> 8) & 0xFF;
				R = (color1 & mask[0]) >> nShiftBits[0];//(color1 >> 16) & 0xFF;
				//G = R; B = R;
				//yuv[i3] = (BYTE)(0.299*R + 0.587*G + 0.114*B);
				//yuv[i4] = (BYTE)(128.0 + 0.36*R - 0.29*G - 0.07*B);
				//yuv[i4] = (BYTE)(128.0 + 0.43*R - 0.36*G - 0.07*B);
				yuv[i3] = (77*R + 150*G + 29*B) >> 8;
				yuv[i4] = (32768 + 110*R - 92*G - 18*B) >> 8;

				pPixels2[offset2++] = *((unsigned __int32 *)yuv);
			}
		}
	}    

    return true;
}

bool CopyRGBSurfaceToRGBSurface(
        LPDDSURFACEDESC2 pddsd1,
        LPDDSURFACEDESC2 pddsd2,
		bool bMMX,
		bool bDisplayWarnings)
{
	char buf[256];

	// currently only works between 2 surfaces with same width, height, *AND color depth.*
    if (pddsd1->dwWidth != pddsd2->dwWidth) 
	{
		sprintf(buf, "%s%s", "ERROR in CopyRGBSurfaceToRGBSurface: ", "surface widths differ!");
		if (bDisplayWarnings) MessageBox(NULL, buf, "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
        return false;
	}
    if (pddsd1->dwHeight != pddsd2->dwHeight) 
	{
		sprintf(buf, "%s%s", "ERROR in CopyRGBSurfaceToRGBSurface: ", "surface heights differ!");
		if (bDisplayWarnings) MessageBox(NULL, buf, "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
        return false;
	}
    if (pddsd1->ddpfPixelFormat.dwRGBBitCount != pddsd2->ddpfPixelFormat.dwRGBBitCount)
	{
		sprintf(buf, "%s%s", "ERROR in CopyRGBSurfaceToRGBSurface: ", "surface bit depths differ!");
		if (bDisplayWarnings) MessageBox(NULL, buf, "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
		return false;
	}

	/*
	if (bDisplayWarnings) 
	{
		int offset  = ((unsigned long)pddsd1) % 8;
		int offset2 = ((unsigned long)pddsd2) % 8;
		char buf[64];
		sprintf(buf, "p1%8=%d, p2%8=%d", offset, offset2);
		MessageBox(NULL, buf, "info", MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
	}
	*/
	
    DWORD  w = pddsd1->dwWidth;
    DWORD  h = pddsd1->dwHeight;
	DWORD  bpp = pddsd1->ddpfPixelFormat.dwRGBBitCount;
    LONG   pitch1 = pddsd1->lPitch;
    LONG   pitch2 = pddsd2->lPitch;
	
	if (pitch1 == pitch2)
	{
		if (bMMX)
			memcpy_MMX(pddsd2->lpSurface, pddsd1->lpSurface, pitch1*h);
		else
			memcpy(pddsd2->lpSurface, pddsd1->lpSurface, pitch1*h);
	}
	else
	{
		int safe_bytes_per_line = min(pitch1, pitch2);
		if (bMMX)
			for (DWORD i=0; i<h; i++)
				memcpy_MMX((void*)((unsigned __int32)pddsd2->lpSurface + i*pitch2), (void*)((unsigned __int32)pddsd1->lpSurface + i*pitch1), safe_bytes_per_line);
		else
			for (DWORD i=0; i<h; i++)
				memcpy(    (void*)((unsigned __int32)pddsd2->lpSurface + i*pitch2), (void*)((unsigned __int32)pddsd1->lpSurface + i*pitch1), safe_bytes_per_line);
	}


	return true;
}

void blit_MMX_with_no_color_space_conversion(LPDDSURFACEDESC2 pddsd1, LPDDSURFACEDESC2 pddsd2)
{
	//int w = min(pddsd1->dwWidth, pddsd2->dwWidth);
	int h = min(pddsd1->dwHeight, pddsd2->dwHeight);
	int bytes_per_scanline = min(pddsd1->lPitch, pddsd2->lPitch);
	int its_per_scanline   = bytes_per_scanline/64;
	int extra_src_bytes_per_scanline  = pddsd2->lPitch - its_per_scanline*64;
	int extra_dest_bytes_per_scanline = pddsd1->lPitch - its_per_scanline*64;
	void *p_dest = pddsd1->lpSurface;
	void *p_src  = pddsd2->lpSurface;

	__asm
	{
		mov edx, h
		mov eax, p_src
		mov edi, p_dest

		ALIGN 16
		mmx_blit_loop_line:

			mov ecx, its_per_scanline

			ALIGN 16
			mmx_blit_loop_64:

				MOVQ  mm0, [eax]   
				MOVQ  mm1, [eax+8] 
				MOVQ  mm2, [eax+16]
				MOVQ  mm3, [eax+24]
				MOVQ  mm4, [eax+32]
				MOVQ  mm5, [eax+40]
				MOVQ  mm6, [eax+48]
				MOVQ  mm7, [eax+56]

				MOVQ  [edi], mm0       
				MOVQ  [edi+8], mm1
				MOVQ  [edi+16], mm2
				MOVQ  [edi+24], mm3
				MOVQ  [edi+32], mm4
				MOVQ  [edi+40], mm5
				MOVQ  [edi+48], mm6
				MOVQ  [edi+56], mm7
            
				ADD   eax, 64
				ADD   edi, 64

				dec	  ecx
				jnz   mmx_blit_loop_64

			ADD  eax, extra_src_bytes_per_scanline
			ADD  edi, extra_dest_bytes_per_scanline
			dec  edx
			jnz  mmx_blit_loop_line

		EMMS
	}	
}


